/*
 * FileInfo.h
 *
 *  Created on: 24.06.2011
 *      Author: mario
 */

#ifndef FILEINFO_H_
#define FILEINFO_H_

// system
#include <string>
#include <queue>
#include <time.h>
#include <boost/shared_ptr.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/foreach.hpp>
#include <fstream>
#include <iostream>
#include <boost/thread.hpp>

// ariba
#include "ariba/utility/system/BlockingMethod.h"

// omnibus
#include "User.h"
#include "Userdatabase.h"
#include "Payload.h"

// interfaces
#include "interfaces/DataReceiverInterface.h"
#include "interfaces/TransferBundleInterface.h"



#define for_each BOOST_FOREACH

using namespace std;
using boost::shared_ptr;
using boost::property_tree::ptree;
using ariba::utility::BlockingMethod;

// TODO ACHTUNG !! nicht lesen und schreiben gleichzeitig benutzen !!

// TODO eigentlich sollte hier mal hin, das ofstreams und ifstreams kann.. (aber momentan tuts auch so..)
typedef shared_ptr<ofstream> ofstream_ptr;
typedef shared_ptr<ifstream> ifstream_ptr;

class FileInfo : public BlockingMethod
{
public:
  FileInfo();
  FileInfo(string key, string filename, uint64_t size, UserPtr partner);
  FileInfo(ptree &pt, Userdatabase* users);
  virtual ~FileInfo();

  string get_key();
  string get_filename();
  uint64_t get_size();
  bool get_acceptance();
  string get_path();
  UserPtr get_partner();
  uint64_t get_transferred();

  void set_acceptance(bool b);
  void set_path(string p);
  void set_size(uint64_t size);
  void change_key(string new_key);
  void skip(uint64_t bytes);
  void set_pause(bool pause);
  void pause(bool just_hold);
  void set_hold(bool hold);
  void hold();
  void seek(uint64_t new_pos);
  void inc_transferred(uint64_t n);

  bool is_complete();
  bool is_paused();
  bool get_try_resume();
  void set_finished();
  void status(ptree &pt);
//  string status_old();  // XXX DEPRECATED

  void save(ptree &pt);

  /* file operations */
  bool request_async_data(DataReceiverInterface* receiver, boost::shared_ptr<FileInfo> self_ref);
  int append(char* buff, int len);
  void file_close();

  bool operator!(void);
    void set_filename(string filename);
    void set_key(string key);
    void set_partner(UserPtr partner);
    TransferBundlePtr get_bundle() const;
    void set_bundle(TransferBundlePtr bundle);


protected:
  /** Blocking Method Interface -- don't call directly !! **/
    virtual void blockingFunction();
    virtual void dispatchFunction();

private:
    bool read_chunk(PayloadPtr pay);

    // measure time consumed in active state
    time_t get_transfer_time();
    void trigger_timing();

private:
    string key;
    string filename;    // XXX Filename ist sozusagen der angezeigte Name. Im Normalfall wird auch der Dateiname daraus erstellt.
    uint64_t size;
    uint64_t transferred;

    UserPtr partner;
    string path;        // XXX Path ist der komplette Dateiname (ggf. relativ) incl. Dateinamen
    bool transfer_accepted;
    bool transfer_paused;
    bool try_resume_paused_transfer;
    
    // timing
    time_t time_consumed;
    time_t unpaused_at;

    ofstream file; // TODO eigentlich sollten die beiden Pointer halt nur einer sein und file heißen...
    ifstream in_file;

    bool finished;

    DataReceiverInterface* receiver;
    deque<PayloadPtr> data_buffer;
    PayloadPtr async_read_data;
    bool async_read_okay;
    int pending_data_requests;
    bool reading;
    bool cancel_reading;
    uint64_t bytes_requested;
    uint64_t bytes_read;
    boost::mutex file_lock; // XXX
    boost::mutex buffer_lock;
    boost::mutex next_lock; // XXX
    bool async_open_file;
    uint64_t read_next;

    uint64_t bytes_acked;
    static const uint64_t FILE_READ_BUFFER_SIZE = 10;
//    static const int CHUNKSIZE = 4096;
    static const int CHUNKSIZE = 2048;
//    static const int CHUNKSIZE = 1024;

    // XXX hack for "cancel problem"
    boost::shared_ptr<FileInfo> self_reference;

    TransferBundlePtr bundle;

public:
//    uint64_t bytes_sent;  // TODO nicht public.. wenn der data-callback in ner funktion ist, passt das da bestimmt genz gut.
};

typedef boost::shared_ptr<FileInfo> FilePtr;

#endif /* FILEINFO_H_ */
